/*-
 * Copyright (c) 2013 Shang-Rong Cai. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $ MTBLAS: mtblas_2_gmv.c    Rev. 0.0.20    2013-10-23T17:52+08:00 $
 */

#include <stdlib.h>
#ifdef TRACE_INFO
#include <stdio.h>
#endif

#include <bscerr.h>

#include <exec_isa.h>
#include <cor_ctrl.h>
#include <mtblas.h>

/* Locally Common Function For Setting Operands Of Matrix-Vector Multiplication */
int mtgmv_opchunks_mv(size_t thrz, int flag, step_t *step_one, step_t *step_fix, step_t *step_lda, step_t *step_incx, addr_t ma, addr_t vx, addr_t buf, size_t r_sz, size_t c_sz, opchunk_t *pk_a, opchunk_t *pk_b, opchunk_t *pk_c)
{
    int ierr = ERRNO_UNDEFINED;

    ierr = opchunks_set_opsize(thrz, pk_a, c_sz);

    ierr = opchunks_set_rb_addr_mono(thrz, pk_a, vx);
    ierr = opchunks_set_rb_step_mono(thrz, pk_a, *step_incx);
    ierr = opchunks_set_rc_addr_dist(thrz, pk_a, r_sz, buf, *step_one);
    ierr = opchunks_set_rc_step_mono(thrz, pk_a, *step_fix);
    ierr = opchunks_set_rd_addr_mono(thrz, pk_a, NULL);
    ierr = opchunks_set_rd_step_mono(thrz, pk_a, *step_fix);

    ierr = opchunks_set_opsize(thrz, pk_b, c_sz);

    ierr = opchunks_set_rb_addr_mono(thrz, pk_b, vx);
    ierr = opchunks_set_rb_step_mono(thrz, pk_b, *step_fix);
    ierr = opchunks_set_rc_addr_dist(thrz, pk_b, r_sz, buf, *step_one);
    ierr = opchunks_set_rc_step_mono(thrz, pk_b, *step_one);
    ierr = opchunks_set_rd_addr_mono(thrz, pk_b, NULL);
    ierr = opchunks_set_rd_step_mono(thrz, pk_b, *step_fix);

    ierr = opchunks_set_ra_step_mono(thrz, pk_c, *step_fix);
    ierr = opchunks_set_rb_addr_mono(thrz, pk_c, step_incx);
    ierr = opchunks_set_rb_step_mono(thrz, pk_c, *step_fix);
    ierr = opchunks_set_rc_addr_mono(thrz, pk_c, step_fix);
    ierr = opchunks_set_rc_step_mono(thrz, pk_c, *step_fix);
    ierr = opchunks_set_rd_addr_mono(thrz, pk_c, step_fix);
    ierr = opchunks_set_rd_step_mono(thrz, pk_c, *step_fix);

    if (flag == GEMV_CATEGORY_0)
    {
        ierr = opchunks_set_ra_addr_dist(thrz, pk_a, r_sz, ma, *step_lda);
        ierr = opchunks_set_ra_step_mono(thrz, pk_a, *step_one);

        ierr = opchunks_set_ra_addr_dist(thrz, pk_b, r_sz, ma, *step_lda);
        ierr = opchunks_set_ra_step_mono(thrz, pk_b, *step_lda);

        ierr = opchunks_set_ra_addr_mono(thrz, pk_c, step_one);
    }
    else
    {
        ierr = opchunks_set_ra_addr_dist(thrz, pk_a, r_sz, ma, *step_one);
        ierr = opchunks_set_ra_step_mono(thrz, pk_a, *step_lda);

        ierr = opchunks_set_ra_addr_dist(thrz, pk_b, r_sz, ma, *step_one);
        ierr = opchunks_set_ra_step_mono(thrz, pk_b, *step_one);

        ierr = opchunks_set_ra_addr_mono(thrz, pk_c, step_lda);
    }

    return ierr;
}


int mtblas_gemvf(size_t thrz, const int rdr, const int trn, const size_t rz, const size_t cz, const float alpha, const float *ma, const size_t lda, const float *vx, const step_t incx, const float beta, float *vy, const step_t incy)
{
    int ierr = ERRNO_UNDEFINED;

    const step_t step_one = sizeof(float);
    const step_t step_fix = 0;
    const step_t step_lda = sizeof(float) * lda;
    const step_t step_incx = sizeof(float) * incx;

    const float buf_init = 0;

    size_t r_sz;  /* Length of result vector */
    size_t c_sz;  /* Length of FMAC */

    int flag;  /* Type of matrix order and transpose */

    float *buf;  /* Buffer vector for storing ma * vx */

    operand_t *ra;  /* Usual operands or point to opchunk to target of executing */
    operand_t *rb;  /* Usual operands or point to modifier of address */
    operand_t *rc;  /* Usual operands or point to modifier of step */

    opchunk_t *pk_a;  /* Target of executing and modifying operations execution pack */
    opchunk_t *pk_b;  /* Modifier of address */
    opchunk_t *pk_c;  /* Modifier of step */

#ifdef TRACE_INFO
    printf("[F] mtblas_gemvf()\n");
#endif

    /* Set Controls */
    ierr = cor_ctrl_gemv_flag(rdr, trn, &flag);

    if (ierr != ERRNO_UNDEFINED)
    {
        goto EXIT_MTBLAS_GEMVF;
    }

    ierr = cor_ctrl_gemv_size(trn, rz, cz, &r_sz, &c_sz);

    thrz = proper_thrz(thrz);

    ierr = operands_create_3(thrz, &ra, &rb, &rc);
    ierr = operands_create_3(thrz, &ra, &rb, &rc);

    ierr = opchunks_create_3(thrz, &pk_a, &pk_b, &pk_c);

    /* Allocate Memory For Buffer: buf[] */
    buf = (float*) calloc(r_sz, sizeof(float));

    /* buf[] = 0 */
    ierr = operands_batch_set(thrz, (void*) &buf_init, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);

    ierr = thread_execute(thrz, (void*) op2_copy_ff, r_sz, ra, rb, NULL, NULL);

    /* buf[] = ma * vx */
    ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_fmac_ff);
    ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_fmac_ff);

    ierr = mtgmv_opchunks_mv(thrz, flag, (step_t*) &step_one, (step_t*) &step_fix, (step_t*) &step_lda, (step_t*) &step_incx, (addr_t) ma, (addr_t) vx, (addr_t) buf, r_sz, c_sz, pk_a, pk_b, pk_c);

    ierr = operands_assign_opchunks(thrz, ra, pk_a);
    ierr = operands_assign_opchunks(thrz, rb, pk_b);
    ierr = operands_assign_opchunks(thrz, rc, pk_c);

    ierr = thread_execute(thrz, op4_exec_op, r_sz, ra, ra, rb, rc);

    /* beta * vy */
    ierr = operands_batch_set(thrz, (void*) &beta, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(float) * incy, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(float) * incy, rc);

    ierr = thread_execute(thrz, op3_mul_ff, r_sz, ra, rb, rc, NULL);

    /* vy += alpha * buf[] */
    ierr = operands_batch_set(thrz, (void*) &alpha, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(float) * incy, rc);

    ierr = thread_execute(thrz, (void*) op3_fmac_ff, r_sz, ra, rb, rc, NULL);

    /* Release Allocated Memory */
    ierr = operands_delete_3(ra, rb, rc);

    ierr = opchunks_delete_3(pk_a, pk_b, pk_c);

    free(buf);

    EXIT_MTBLAS_GEMVF:
#ifdef TRACE_INFO
    printf("    mtblas_gemvf() Passed!\n");
#endif

    return ierr;
}


int mtblas_gemv(size_t thrz, const int rdr, const int trn, const size_t rz, const size_t cz, const double alpha, const double *ma, const size_t lda, const double *vx, const step_t incx, const double beta, double *vy, const step_t incy)
{
    int ierr = ERRNO_UNDEFINED;

    const step_t step_one = sizeof(double);
    const step_t step_fix = 0;
    const step_t step_lda = sizeof(double) * lda;
    const step_t step_incx = sizeof(double) * incx;

    const double buf_init = 0;

    size_t r_sz;  /* Length of result vector */
    size_t c_sz;  /* Length of FMAC */

    int flag;  /* Type of matrix order and transpose */

    double *buf;  /* Buffer vector for storing ma * vx */

    operand_t *ra;  /* Usual operands or point to opchunk to target of executing */
    operand_t *rb;  /* Usual operands or point to modifier of address */
    operand_t *rc;  /* Usual operands or point to modifier of step */

    opchunk_t *pk_a;  /* Target of executing and modifying operations execution pack */
    opchunk_t *pk_b;  /* Modifier of address */
    opchunk_t *pk_c;  /* Modifier of step */

#ifdef TRACE_INFO
    printf("[F] mtblas_gemv()\n");
#endif

    /* Set Controls */
    ierr = cor_ctrl_gemv_flag(rdr, trn, &flag);

    if (ierr != ERRNO_UNDEFINED)
    {
        goto EXIT_MTBLAS_GEMV;
    }

    ierr = cor_ctrl_gemv_size(trn, rz, cz, &r_sz, &c_sz);

    thrz = proper_thrz(thrz);

    ierr = operands_create_3(thrz, &ra, &rb, &rc);
    ierr = operands_create_3(thrz, &ra, &rb, &rc);

    ierr = opchunks_create_3(thrz, &pk_a, &pk_b, &pk_c);

    /* Allocate Memory For Buffer: buf[] */
    buf = (double*) calloc(r_sz, sizeof(double));

    /* buf[] = 0 */
    ierr = operands_batch_set(thrz, (void*) &buf_init, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);

    ierr = thread_execute(thrz, (void*) op2_copy_df, r_sz, ra, rb, NULL, NULL);

    /* buf[] = ma * vx */
    ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_fmac_df);
    ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_fmac_df);

    ierr = mtgmv_opchunks_mv(thrz, flag, (step_t*) &step_one, (step_t*) &step_fix, (step_t*) &step_lda, (step_t*) &step_incx, (addr_t) ma, (addr_t) vx, (addr_t) buf, r_sz, c_sz, pk_a, pk_b, pk_c);

    ierr = operands_assign_opchunks(thrz, ra, pk_a);
    ierr = operands_assign_opchunks(thrz, rb, pk_b);
    ierr = operands_assign_opchunks(thrz, rc, pk_c);

    ierr = thread_execute(thrz, op4_exec_op, r_sz, ra, ra, rb, rc);

    /* beta * vy */
    ierr = operands_batch_set(thrz, (void*) &beta, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(double) * incy, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(double) * incy, rc);

    ierr = thread_execute(thrz, op3_mul_df, r_sz, ra, rb, rc, NULL);

    /* vy += alpha * buf[] */
    ierr = operands_batch_set(thrz, (void*) &alpha, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(double) * incy, rc);

    ierr = thread_execute(thrz, (void*) op3_fmac_df, r_sz, ra, rb, rc, NULL);

    /* Release Allocated Memory */
    ierr = operands_delete_3(ra, rb, rc);

    ierr = opchunks_delete_3(pk_a, pk_b, pk_c);

    free(buf);

    EXIT_MTBLAS_GEMV:
#ifdef TRACE_INFO
    printf("    mtblas_gemv() Passed!\n");
#endif

    return ierr;
}


int mtblas_gemvl(size_t thrz, const int rdr, const int trn, const size_t rz, const size_t cz, const long double alpha, const long double *ma, const size_t lda, const long double *vx, const step_t incx, const long double beta, long double *vy, const step_t incy)
{
    int ierr = ERRNO_UNDEFINED;

    const step_t step_one = sizeof(long double);
    const step_t step_fix = 0;
    const step_t step_lda = sizeof(long double) * lda;
    const step_t step_incx = sizeof(long double) * incx;

    const long double buf_init = 0;

    size_t r_sz;  /* Length of result vector */
    size_t c_sz;  /* Length of FMAC */

    int flag;  /* Type of matrix order and transpose */

    long double *buf;  /* Buffer vector for storing ma * vx */

    operand_t *ra;  /* Usual operands or point to opchunk to target of executing */
    operand_t *rb;  /* Usual operands or point to modifier of address */
    operand_t *rc;  /* Usual operands or point to modifier of step */

    opchunk_t *pk_a;  /* Target of executing and modifying operations execution pack */
    opchunk_t *pk_b;  /* Modifier of address */
    opchunk_t *pk_c;  /* Modifier of step */

#ifdef TRACE_INFO
    printf("[F] mtblas_gemvl()\n");
#endif

    /* Set Controls */
    ierr = cor_ctrl_gemv_flag(rdr, trn, &flag);

    if (ierr != ERRNO_UNDEFINED)
    {
        goto EXIT_MTBLAS_GEMVL;
    }

    ierr = cor_ctrl_gemv_size(trn, rz, cz, &r_sz, &c_sz);

    thrz = proper_thrz(thrz);

    ierr = operands_create_3(thrz, &ra, &rb, &rc);
    ierr = operands_create_3(thrz, &ra, &rb, &rc);

    ierr = opchunks_create_3(thrz, &pk_a, &pk_b, &pk_c);

    /* Allocate Memory For Buffer: buf[] */
    buf = (long double*) calloc(r_sz, sizeof(long double));

    /* buf[] = 0 */
    ierr = operands_batch_set(thrz, (void*) &buf_init, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);

    ierr = thread_execute(thrz, (void*) op2_copy_lf, r_sz, ra, rb, NULL, NULL);

    /* buf[] = ma * vx */
    ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_fmac_lf);
    ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_fmac_lf);

    ierr = mtgmv_opchunks_mv(thrz, flag, (step_t*) &step_one, (step_t*) &step_fix, (step_t*) &step_lda, (step_t*) &step_incx, (addr_t) ma, (addr_t) vx, (addr_t) buf, r_sz, c_sz, pk_a, pk_b, pk_c);

    ierr = operands_assign_opchunks(thrz, ra, pk_a);
    ierr = operands_assign_opchunks(thrz, rb, pk_b);
    ierr = operands_assign_opchunks(thrz, rc, pk_c);

    ierr = thread_execute(thrz, op4_exec_op, r_sz, ra, ra, rb, rc);

    /* beta * vy */
    ierr = operands_batch_set(thrz, (void*) &beta, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(long double) * incy, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(long double) * incy, rc);

    ierr = thread_execute(thrz, op3_mul_lf, r_sz, ra, rb, rc, NULL);

    /* vy += alpha * buf[] */
    ierr = operands_batch_set(thrz, (void*) &alpha, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(long double) * incy, rc);

    ierr = thread_execute(thrz, (void*) op3_fmac_lf, r_sz, ra, rb, rc, NULL);

    /* Release Allocated Memory */
    ierr = operands_delete_3(ra, rb, rc);

    ierr = opchunks_delete_3(pk_a, pk_b, pk_c);

    free(buf);

    EXIT_MTBLAS_GEMVL:
#ifdef TRACE_INFO
    printf("    mtblas_gemvl() Passed!\n");
#endif

    return ierr;
}


int mtblas_cgemvf(size_t thrz, const int rdr, const int trn, const size_t rz, const size_t cz, const float complex alpha, const float complex *ma, const size_t lda, const float complex *vx, const step_t incx, const float complex beta, float complex *vy, const step_t incy)
{
    int ierr = ERRNO_UNDEFINED;

    const step_t step_one = sizeof(float complex);
    const step_t step_fix = 0;
    const step_t step_lda = sizeof(float complex) * lda;
    const step_t step_incx = sizeof(float complex) * incx;

    const float complex buf_init = 0;

    size_t r_sz;  /* Length of result vector */
    size_t c_sz;  /* Length of FMAC */

    int flag;  /* Type of matrix order and transpose */

    float complex *buf;  /* Buffer vector for storing ma * vx */

    operand_t *ra;  /* Usual operands or point to opchunk to target of executing */
    operand_t *rb;  /* Usual operands or point to modifier of address */
    operand_t *rc;  /* Usual operands or point to modifier of step */

    opchunk_t *pk_a;  /* Target of executing and modifying operations execution pack */
    opchunk_t *pk_b;  /* Modifier of address */
    opchunk_t *pk_c;  /* Modifier of step */

#ifdef TRACE_INFO
    printf("[F] mtblas_cgemvf()\n");
#endif

    /* Set Controls */
    ierr = cor_ctrl_gemv_flag(rdr, trn, &flag);

    if (ierr != ERRNO_UNDEFINED)
    {
        goto EXIT_MTBLAS_CGEMVF;
    }

    ierr = cor_ctrl_gemv_size(trn, rz, cz, &r_sz, &c_sz);

    thrz = proper_thrz(thrz);

    ierr = operands_create_3(thrz, &ra, &rb, &rc);
    ierr = operands_create_3(thrz, &ra, &rb, &rc);

    ierr = opchunks_create_3(thrz, &pk_a, &pk_b, &pk_c);

    /* Allocate Memory For Buffer: buf[] */
    buf = (float complex*) calloc(r_sz, sizeof(float complex));

    /* buf[] = 0 */
    ierr = operands_batch_set(thrz, (void*) &buf_init, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);

    ierr = thread_execute(thrz, (void*) op2_copy_fc, r_sz, ra, rb, NULL, NULL);

    /* buf[] = ma * vx */
    if (trn == MTBLAS_TRANS_CONJ)
    {
        ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_conjfmac_fc);
        ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_conjfmac_fc);
    }
    else
    {
        ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_fmac_fc);
        ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_fmac_fc);
    }

    ierr = mtgmv_opchunks_mv(thrz, flag, (step_t*) &step_one, (step_t*) &step_fix, (step_t*) &step_lda, (step_t*) &step_incx, (addr_t) ma, (addr_t) vx, (addr_t) buf, r_sz, c_sz, pk_a, pk_b, pk_c);

    ierr = operands_assign_opchunks(thrz, ra, pk_a);
    ierr = operands_assign_opchunks(thrz, rb, pk_b);
    ierr = operands_assign_opchunks(thrz, rc, pk_c);

    ierr = thread_execute(thrz, op4_exec_op, r_sz, ra, ra, rb, rc);

    /* beta * vy */
    ierr = operands_batch_set(thrz, (void*) &beta, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(float complex) * incy, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(float complex) * incy, rc);

    ierr = thread_execute(thrz, op3_mul_fc, r_sz, ra, rb, rc, NULL);

    /* vy += alpha * buf[] */
    ierr = operands_batch_set(thrz, (void*) &alpha, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(float complex) * incy, rc);

    ierr = thread_execute(thrz, (void*) op3_fmac_fc, r_sz, ra, rb, rc, NULL);

    /* Release Allocated Memory */
    ierr = operands_delete_3(ra, rb, rc);

    ierr = opchunks_delete_3(pk_a, pk_b, pk_c);

    free(buf);

    EXIT_MTBLAS_CGEMVF:
#ifdef TRACE_INFO
    printf("    mtblas_cgemvf() Passed!\n");
#endif

    return ierr;
}


int mtblas_cgemv(size_t thrz, const int rdr, const int trn, const size_t rz, const size_t cz, const double complex alpha, const double complex *ma, const size_t lda, const double complex *vx, const step_t incx, const double complex beta, double complex *vy, const step_t incy)
{
    int ierr = ERRNO_UNDEFINED;

    const step_t step_one = sizeof(double complex);
    const step_t step_fix = 0;
    const step_t step_lda = sizeof(double complex) * lda;
    const step_t step_incx = sizeof(double complex) * incx;

    const double complex buf_init = 0;

    size_t r_sz;  /* Length of result vector */
    size_t c_sz;  /* Length of FMAC */

    int flag;  /* Type of matrix order and transpose */

    double complex *buf;  /* Buffer vector for storing ma * vx */

    operand_t *ra;  /* Usual operands or point to opchunk to target of executing */
    operand_t *rb;  /* Usual operands or point to modifier of address */
    operand_t *rc;  /* Usual operands or point to modifier of step */

    opchunk_t *pk_a;  /* Target of executing and modifying operations execution pack */
    opchunk_t *pk_b;  /* Modifier of address */
    opchunk_t *pk_c;  /* Modifier of step */

#ifdef TRACE_INFO
    printf("[F] mtblas_cgemv()\n");
#endif

    /* Set Controls */
    ierr = cor_ctrl_gemv_flag(rdr, trn, &flag);

    if (ierr != ERRNO_UNDEFINED)
    {
        goto EXIT_MTBLAS_CGEMV;
    }

    ierr = cor_ctrl_gemv_size(trn, rz, cz, &r_sz, &c_sz);

    thrz = proper_thrz(thrz);

    ierr = operands_create_3(thrz, &ra, &rb, &rc);
    ierr = operands_create_3(thrz, &ra, &rb, &rc);

    ierr = opchunks_create_3(thrz, &pk_a, &pk_b, &pk_c);

    /* Allocate Memory For Buffer: buf[] */
    buf = (double complex*) calloc(r_sz, sizeof(double complex));

    /* buf[] = 0 */
    ierr = operands_batch_set(thrz, (void*) &buf_init, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);

    ierr = thread_execute(thrz, (void*) op2_copy_dc, r_sz, ra, rb, NULL, NULL);

    /* buf[] = ma * vx */
    if (trn == MTBLAS_TRANS_CONJ)
    {
        ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_conjfmac_dc);
        ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_conjfmac_dc);
    }
    else
    {
        ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_fmac_dc);
        ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_fmac_dc);
    }

    ierr = mtgmv_opchunks_mv(thrz, flag, (step_t*) &step_one, (step_t*) &step_fix, (step_t*) &step_lda, (step_t*) &step_incx, (addr_t) ma, (addr_t) vx, (addr_t) buf, r_sz, c_sz, pk_a, pk_b, pk_c);

    ierr = operands_assign_opchunks(thrz, ra, pk_a);
    ierr = operands_assign_opchunks(thrz, rb, pk_b);
    ierr = operands_assign_opchunks(thrz, rc, pk_c);

    ierr = thread_execute(thrz, op4_exec_op, r_sz, ra, ra, rb, rc);

    /* beta * vy */
    ierr = operands_batch_set(thrz, (void*) &beta, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(double complex) * incy, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(double complex) * incy, rc);

    ierr = thread_execute(thrz, op3_mul_dc, r_sz, ra, rb, rc, NULL);

    /* vy += alpha * buf[] */
    ierr = operands_batch_set(thrz, (void*) &alpha, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(double complex) * incy, rc);

    ierr = thread_execute(thrz, (void*) op3_fmac_dc, r_sz, ra, rb, rc, NULL);

    /* Release Allocated Memory */
    ierr = operands_delete_3(ra, rb, rc);

    ierr = opchunks_delete_3(pk_a, pk_b, pk_c);

    free(buf);

    EXIT_MTBLAS_CGEMV:
#ifdef TRACE_INFO
    printf("    mtblas_cgemv() Passed!\n");
#endif

    return ierr;
}


int mtblas_cgemvl(size_t thrz, const int rdr, const int trn, const size_t rz, const size_t cz, const long double complex alpha, const long double complex *ma, const size_t lda, const long double complex *vx, const step_t incx, const long double complex beta, long double complex *vy, const step_t incy)
{
    int ierr = ERRNO_UNDEFINED;

    const step_t step_one = sizeof(long double complex);
    const step_t step_fix = 0;
    const step_t step_lda = sizeof(long double complex) * lda;
    const step_t step_incx = sizeof(long double complex) * incx;

    const long double complex buf_init = 0;

    size_t r_sz;  /* Length of result vector */
    size_t c_sz;  /* Length of FMAC */

    int flag;  /* Type of matrix order and transpose */

    long double complex *buf;  /* Buffer vector for storing ma * vx */

    operand_t *ra;  /* Usual operands or point to opchunk to target of executing */
    operand_t *rb;  /* Usual operands or point to modifier of address */
    operand_t *rc;  /* Usual operands or point to modifier of step */

    opchunk_t *pk_a;  /* Target of executing and modifying operations execution pack */
    opchunk_t *pk_b;  /* Modifier of address */
    opchunk_t *pk_c;  /* Modifier of step */

#ifdef TRACE_INFO
    printf("[F] mtblas_cgemvl()\n");
#endif

    /* Set Controls */
    ierr = cor_ctrl_gemv_flag(rdr, trn, &flag);

    if (ierr != ERRNO_UNDEFINED)
    {
        goto EXIT_MTBLAS_CGEMVL;
    }

    ierr = cor_ctrl_gemv_size(trn, rz, cz, &r_sz, &c_sz);

    thrz = proper_thrz(thrz);

    ierr = operands_create_3(thrz, &ra, &rb, &rc);
    ierr = operands_create_3(thrz, &ra, &rb, &rc);

    ierr = opchunks_create_3(thrz, &pk_a, &pk_b, &pk_c);

    /* Allocate Memory For Buffer: buf[] */
    buf = (long double complex*) calloc(r_sz, sizeof(long double complex));

    /* buf[] = 0 */
    ierr = operands_batch_set(thrz, (void*) &buf_init, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);

    ierr = thread_execute(thrz, (void*) op2_copy_lc, r_sz, ra, rb, NULL, NULL);

    /* buf[] = ma * vx */
    if (trn == MTBLAS_TRANS_CONJ)
    {
        ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_conjfmac_lc);
        ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_conjfmac_lc);
    }
    else
    {
        ierr = opchunks_set_opcode(thrz, pk_a, (void*) op3_fmac_lc);
        ierr = opchunks_set_opcode(thrz, pk_b, (void*) op3_fmac_lc);
    }

    ierr = mtgmv_opchunks_mv(thrz, flag, (step_t*) &step_one, (step_t*) &step_fix, (step_t*) &step_lda, (step_t*) &step_incx, (addr_t) ma, (addr_t) vx, (addr_t) buf, r_sz, c_sz, pk_a, pk_b, pk_c);

    ierr = operands_assign_opchunks(thrz, ra, pk_a);
    ierr = operands_assign_opchunks(thrz, rb, pk_b);
    ierr = operands_assign_opchunks(thrz, rc, pk_c);

    ierr = thread_execute(thrz, op4_exec_op, r_sz, ra, ra, rb, rc);

    /* beta * vy */
    ierr = operands_batch_set(thrz, (void*) &beta, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(long double complex) * incy, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(long double complex) * incy, rc);

    ierr = thread_execute(thrz, op3_mul_lc, r_sz, ra, rb, rc, NULL);

    /* vy += alpha * buf[] */
    ierr = operands_batch_set(thrz, (void*) &alpha, step_fix, ra);
    ierr = operands_distributor(thrz, r_sz, (void*) buf, step_one, rb);
    ierr = operands_distributor(thrz, r_sz, (void*) vy, sizeof(long double complex) * incy, rc);

    ierr = thread_execute(thrz, (void*) op3_fmac_lc, r_sz, ra, rb, rc, NULL);

    /* Release Allocated Memory */
    ierr = operands_delete_3(ra, rb, rc);

    ierr = opchunks_delete_3(pk_a, pk_b, pk_c);

    free(buf);

    EXIT_MTBLAS_CGEMVL:
#ifdef TRACE_INFO
    printf("    mtblas_cgemvl() Passed!\n");
#endif

    return ierr;
}
